package com.yuandian.management.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuandian.api.management.entity.ConfMpExternalPurchase;
import com.yuandian.api.management.po.ConfMpPurchaseParams;
import com.yuandian.api.management.vo.ConfMpExternalPurchaseExcelVo;
import com.yuandian.bpm.common.core.util.R;
import com.yuandian.bpm.common.excel.vo.ErrorMessage;
import com.yuandian.management.mapper.ConfMpExternalPurchaseMapper;
import com.yuandian.management.service.ConfMpExternalPurchaseService;
import com.yuandian.management.service.ConfMpServiceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 中台监控-外部订购单配置-Service实现
 * @author zfsn
 * @date 2023-4-20
 */
@Service
public class ConfMpExternalPurchaseServiceImpl extends ServiceImpl<ConfMpExternalPurchaseMapper, ConfMpExternalPurchase>
		implements ConfMpExternalPurchaseService {

	@Autowired
	private ConfMpServiceService confMpServiceService;

	/**
	 * 通过唯一性编码组合获取订购单配置
	 * @param abilityCode 能力编码
	 * @param userOrgCode 能力使用方单位编码
	 * @return 订购单配置实体
	 */
	private ConfMpExternalPurchase getByUniqueCode(String abilityCode, String userOrgCode) {
		return lambdaQuery().eq(ConfMpExternalPurchase::getAbilityCode, abilityCode)
				.eq(ConfMpExternalPurchase::getUserOrgCode, userOrgCode).one();
	}

	/**
	 * 分页查询外部订购单
	 * @param params 中台监控-订购单配置请求参数
	 * @return 通用返回对象
	 */
	@Override
	public R<Page<ConfMpExternalPurchase>> externalPage(ConfMpPurchaseParams<ConfMpExternalPurchase> params) {
		Page<ConfMpExternalPurchase> page = Page.of(params.getCurrent(), params.getSize());
		page.addOrder(params.orders()).orders().forEach(orderItem -> orderItem.setColumn(StrUtil.toUnderlineCase(orderItem.getColumn())));
		return R.ok(lambdaQuery()
				.like(StrUtil.isNotBlank(params.getAbilityCode()), ConfMpExternalPurchase::getAbilityCode, params.getAbilityCode())
				.like(StrUtil.isNotBlank(params.getAbilityName()), ConfMpExternalPurchase::getAbilityName, params.getAbilityName())
				.page(page));
	}

	/**
	 * 新增外部订购单
	 * @param params 中台监控-单位配置请求参数
	 * @return 通用返回对象
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public R<Boolean> externalSave(ConfMpPurchaseParams<ConfMpExternalPurchase> params) {
		// 1、校验编码组合唯一性
		if (ObjectUtil.isNotNull(getByUniqueCode(params.getAbilityCode(), params.getUserOrgCode()))) {
			return R.failed("新增失败，已存在相同的能力编码 + 能力使用方单位编码！");
		}
		// 2、新增订购单配置
		ConfMpExternalPurchase confMpExternalPurchase = BeanUtil.copyProperties(params, ConfMpExternalPurchase.class);
		// 3、切分能力所属的中台和单位
		String abilityCode = confMpExternalPurchase.getAbilityCode();
		// 能力编码中，  单位：第3位~第5位
		confMpExternalPurchase.setProviderOrgCode(abilityCode.substring(2, 5));
		Map<String, String> map = confMpServiceService.getOrg();
		confMpExternalPurchase.setProviderOrgName(map.get(confMpExternalPurchase.getProviderOrgCode()));
		save(confMpExternalPurchase);
		return R.ok(Boolean.TRUE, "新增成功");
	}

	/**
	 * 主键修改外部订购单
	 * @param params 中台监控-单位配置请求参数
	 * @return 通用返回对象
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public R<Boolean> externalUpdateById(ConfMpPurchaseParams<ConfMpExternalPurchase> params) {
		// 1、校验请求参数
		Integer id = params.getId();
		if (id == null) {
			return R.failed("更新失败，订购单id不能为空！");
		}
		// 2、通过id获取订购单配置
		ConfMpExternalPurchase purchase = getById(id);
		// 3、校验该配置是否存在
		if (ObjectUtil.isNull(purchase)) {
			return R.failed("更新失败，不存在该订购单配置！");
		}
		// 4、校验编码组合的唯一性
		ConfMpExternalPurchase purchaseByUniqueCode = getByUniqueCode(params.getAbilityCode(), params.getUserOrgCode());
		if (ObjectUtil.isNotNull(purchaseByUniqueCode) && !id.equals(purchaseByUniqueCode.getId())) {
			return R.failed("新增失败，已存在相同的能力编码 + 能力使用方单位编码！");
		}
		// 5、更新订购单配置
		BeanUtil.copyProperties(params, purchase);
		updateById(purchase);
		return R.ok(Boolean.TRUE, "更新成功");
	}

	/**
	 * 主键删除外部订购单
	 * @param idList 主键集合
	 * @return 通用返回对象
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public R<Boolean> externalRemoveByIds(List<Integer> idList) {
		// 1、校验请求参数
		if (CollUtil.isEmpty(idList)) {
			return R.failed("删除失败，主键集合不能为空");
		}
		// 2、删除订购单配置
		removeByIds(idList);
		return R.ok(Boolean.TRUE, "删除成功");
	}

	/**
	 * 导入外部订购单配置
	 * @param excelVoList 导入的数据集
	 * @param bindingResult 用来校验 实体 jsr303 校验失败结果
	 * @return 通用返回对象
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public R<List<ErrorMessage>> externalImport(List<ConfMpExternalPurchaseExcelVo> excelVoList, BindingResult bindingResult) {
		// 1、校验请求参数
		if (CollUtil.isEmpty(excelVoList)) {
			return R.failed("导入失败，Excel为空或解析失败！");
		}
		// 2、获取订购单配置唯一性校验集
		Map<String, Integer> uniqueExternalMap = lambdaQuery()
				.select(ConfMpExternalPurchase::getId,
						ConfMpExternalPurchase::getAbilityCode,
						ConfMpExternalPurchase::getUserOrgCode)
				.list()
				.stream()
				.collect(Collectors.toMap(externalPurchase -> externalPurchase.getAbilityCode()
						+ "-" + externalPurchase.getUserOrgCode(), ConfMpExternalPurchase::getId, (k1, k2) -> k1));
		// 3、遍历导入的数据集
		long failCount = 0;
		List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
		assert errorMessageList != null;
		Map<String, ConfMpExternalPurchase> map = new HashMap<>();
		for (ConfMpExternalPurchaseExcelVo purchase : excelVoList) {
			Set<String> errorMsg = new HashSet<>();
			String abilityCode = purchase.getAbilityCode();
			String abilityName = purchase.getAbilityName();
			String providerOrgCode = purchase.getProviderOrgCode();
			String providerOrgName = purchase.getProviderOrgName();
			String userOrgCode = purchase.getUserOrgCode();
			String userOrgName = purchase.getUserOrgName();
			// 非空校验
			if (!StrUtil.isAllNotBlank(abilityCode, abilityName, providerOrgCode, providerOrgName, userOrgCode, userOrgName)) {
				errorMsg.add("非空校验不通过，请检查以下字段：能力名称、能力编码、能力提供方单位名称、能力提供方单位编码、能力使用方单位名称、能力使用方单位编码");
				errorMessageList.add(new ErrorMessage(purchase.getLineNum(), errorMsg));
				failCount++;
				continue;
			}
			ConfMpExternalPurchase.ConfMpExternalPurchaseBuilder builder = ConfMpExternalPurchase.builder();
			// 唯一校验：能力编码 + 能力使用方单位编码
			String key = abilityCode + "-" + userOrgCode;
			Integer id = uniqueExternalMap.get(key);
			if (id != null) {
				// 存在重复的编码组合，订购单的处理方式是覆盖更新，所以这里创建实体对象时需要回填id
				builder.id(id);
			}
			// 通过校验，创建实体对象添加到待入库集合中，这里用map的作用是相同的key，后面的会覆盖前面的，起到覆盖更新的作用
			map.put(key, builder
					.abilityCode(abilityCode)
					.abilityName(abilityName)
					.providerOrgCode(providerOrgCode)
					.providerOrgName(providerOrgName)
					.userOrgCode(userOrgCode)
					.userOrgName(userOrgName)
					.build());
		}
		// 5、批量写入外部订购单配置表
		Collection<ConfMpExternalPurchase> list = map.values();
		if (CollUtil.isNotEmpty(list)) {
			saveOrUpdateBatch(list, 1000);
		}
		return R.ok(errorMessageList, "导入完成，失败条数：" + failCount);
	}
}